import { globby } from 'globby'
import ts from 'typescript'

import { writeFileAsync } from '../packages/common/src/write-file-async.js'

async function main(): Promise<void> {
  const globPatterns = process.argv.slice(2)
  try {
    await generateIndexTs(globPatterns, 'src/index.ts')
  } catch (error: any) {
    console.error(error.message) // eslint-disable-line no-console
    process.exit(1)
  }
}
main()

async function generateIndexTs(
  globPatterns: Array<string>,
  outputFilePath: string
): Promise<void> {
  const filePaths = await globby([
    ...globPatterns,
    `!${outputFilePath}`,
    '!**/*.d.ts',
    '!**/*.stories.tsx'
  ])
  const result: Array<string> = [] // Array of export declaration strings
  const usedExportNames: Record<string, true> = {} // Track the names of exports that were already used
  const program = ts.createProgram(filePaths, { allowJs: true })
  for (const filePath of filePaths) {
    const sourceFile = program.getSourceFile(filePath)
    if (typeof sourceFile === 'undefined') {
      throw new Error(`\`sourceFile\` is \`undefined\`: ${filePath}`)
    }
    const exportNames: Array<string> = []
    function addExport(exportName: string) {
      if (usedExportNames[exportName] === true) {
        throw new Error(`Export name clash \`${exportName}\`: ${filePath}`)
      }
      usedExportNames[exportName] = true
      exportNames.push(exportName)
    }
    ts.forEachChild(sourceFile, function (node: ts.Node): void {
      if (
        ts.isTypeAliasDeclaration(node) ||
        ts.isInterfaceDeclaration(node) ||
        ts.isFunctionDeclaration(node)
      ) {
        if (
          typeof node.modifiers !== 'undefined' &&
          node.modifiers[0].kind === ts.SyntaxKind.ExportKeyword &&
          typeof node.name !== 'undefined'
        ) {
          if (
            node.modifiers.length > 1 &&
            node.modifiers[1].kind === ts.SyntaxKind.DefaultKeyword
          ) {
            throw new Error(`Use of \`default\` export detected: ${filePath}`)
          }
          addExport(node.name.text)
        }
      }
      if (ts.isVariableStatement(node)) {
        if (
          typeof node.modifiers !== 'undefined' &&
          node.modifiers[0].kind === ts.SyntaxKind.ExportKeyword
        ) {
          const identifier = node.declarationList.declarations[0].name
          if (ts.isIdentifier(identifier)) {
            addExport(identifier.text)
          }
        }
      }
      if (ts.isModuleDeclaration(node)) {
        if (
          typeof node.modifiers !== 'undefined' &&
          node.modifiers[0].kind === ts.SyntaxKind.ExportKeyword
        ) {
          addExport(node.name.text)
        }
      }
    })
    const normalizedFilePath = filePath
      .replace(/^(?:\.\/)?src\//, './') // Relace `./src/` with `./`
      .replace(/\.tsx?/, '.js')
    const exportDeclaration = `export { ${exportNames
      .sort()
      .join(', ')} } from '${normalizedFilePath}'`
    result.push(exportDeclaration)
  }
  await writeFileAsync(
    outputFilePath,
    `// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n${result.join(
      '\n'
    )}`
  )
}
